Passed
Push — master ( 8bb50e...3e923a )
by Rafael S.
02:31
created

T_EXPORT ➔ write32F_   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
c 0
b 0
f 0
cc 1
nc 1
nop 3
rs 10
1
/*
2
 * byte-data: Pack and unpack binary data.
3
 * https://github.com/rochars/byte-data
4
 *
5
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
6
 *
7
 * Permission is hereby granted, free of charge, to any person obtaining
8
 * a copy of this software and associated documentation files (the
9
 * "Software"), to deal in the Software without restriction, including
10
 * without limitation the rights to use, copy, modify, merge, publish,
11
 * distribute, sublicense, and/or sell copies of the Software, and to
12
 * permit persons to whom the Software is furnished to do so, subject to
13
 * the following conditions:
14
 *
15
 * The above copyright notice and this permission notice shall be
16
 * included in all copies or substantial portions of the Software.
17
 *
18
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
 *
26
 */
27
28
/**
29
 * @fileoverview The byte-data API.
30
 */
31
32
/** @module byteData */
33
34
import Integer from './lib/integer.js';
35
36
import endianness from 'endianness';
37
38
export {default as types} from './lib/types.js';
39
40
/**
41
 * Pack a number or a string as a byte buffer.
42
 * @param {number|string} value The value.
43
 * @param {!Object} theType The type definition.
44
 * @return {!Array<number>}
45
 * @throws {Error} If the type definition is not valid.
46
 * @throws {Error} If the value is not valid.
47
 */
48
export function pack(value, theType) {
49
  setUp_(theType);
50
  return toBytes_([value], theType);
51
}
52
53
/**
54
 * Pack an array of numbers or strings to a byte buffer.
55
 * @param {!Array<number|string>|string} values The values.
56
 * @param {!Object} theType The type definition.
57
 * @return {!Array<number>}
58
 * @throws {Error} If the type definition is not valid.
59
 * @throws {Error} If any of the values are not valid.
60
 */
61
export function packArray(values, theType) {
62
  setUp_(theType);
63
  return toBytes_(values, theType);
64
}
65
66
/**
67
 * Pack a number or a string to a existing byte buffer.
68
 * @param {number|string} value The value.
69
 * @param {!Object} theType The type definition.
70
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
71
 * @param {number} index The buffer index to write.
72
 * @return {number} The next index to start writing.
73
 * @throws {Error} If the type definition is not valid.
74
 * @throws {Error} If the value is not valid.
75
 */
76
export function packTo(value, theType, buffer, index) {
77
  setUp_(theType);
78
  let validate = validateNotNull_;
79
  if (theType['char']) {
80
    validate = validateString_;
81
  }
82
  return writeBytes_(value,
83
    theType,
84
    buffer,
85
    index,
86
    index + theType['offset'],
87
    validate,
88
    theType['be']);
89
}
90
91
/**
92
 * Pack a array of numbers or strings to a existing byte buffer.
93
 * @param {!Array<number|string>} values The value.
94
 * @param {!Object} theType The type definition.
95
 * @param {!Uint8Array|!Array<number>} buffer The output buffer.
96
 * @param {number} index The buffer index to write.
97
 * @return {number} The next index to start writing.
98
 * @throws {Error} If the type definition is not valid.
99
 * @throws {Error} If the value is not valid.
100
 */
101
export function packArrayTo(values, theType, buffer, index) {
102
  setUp_(theType);
103
  let validate = validateNotNull_;
104
  if (theType['char']) {
105
    validate = validateString_;
106
  }
107
  let be = theType['be'];
108
  let offset = theType['offset'];
109
  for (let i=0; i<values.length; i++) {
110
    index = writeBytes_(
111
      values[i],
112
      theType,
113
      buffer,
114
      index,
115
      index + offset,
116
      validate, be);
117
  }
118
  return index;
119
}
120
121
/**
122
 * Unpack a number or a string from a byte buffer.
123
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
124
 * @param {!Object} theType The type definition.
125
 * @return {number|string}
126
 * @throws {Error} If the type definition is not valid
127
 */
128
export function unpack(buffer, theType) {
129
  setUp_(theType);
130
  let values = fromBytes_(
131
    buffer.slice(0, theType['offset']), theType);
132
  return values[0];
133
}
134
135
/**
136
 * Unpack an array of numbers or strings from a byte buffer.
137
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
138
 * @param {!Object} theType The type definition.
139
 * @return {!Array<number|string>}
140
 * @throws {Error} If the type definition is not valid.
141
 */
142
export function unpackArray(buffer, theType) {
143
  setUp_(theType);
144
  return fromBytes_(buffer, theType);
145
}
146
147
/**
148
 * Unpack a number or a string from a byte buffer index.
149
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
150
 * @param {!Object} theType The type definition.
151
 * @param {number=} index The buffer index to read.
152
 * @return {number|string}
153
 * @throws {Error} If the type definition is not valid
154
 */
155
export function unpackFrom(buffer, theType, index=0) {
156
  setUp_(theType);
157
  return readBytes_(buffer, theType, index);
158
}
159
160
/**
161
 * Unpack a array of numbers strings from a byte buffer index.
162
 * @param {!Array<number>|!Uint8Array} buffer The byte buffer.
163
 * @param {!Object} theType The type definition.
164
 * @param {number=} start The start index. Assumes 0.
165
 * @param {?number=} end The end index. Assumes the array length.
166
 * @return {!Array<number>}
167
 * @throws {Error} If the type definition is not valid
168
 */
169
export function unpackArrayFrom(buffer, theType, start=0, end=null) {
170
  setUp_(theType);
171
  if (theType['be']) {
172
    endianness(buffer, theType['offset']);
173
  }
174
  let len = end || buffer.length;
175
  let values = [];
176
  for (let i=start; i<len; i+=theType['offset']) {
177
    values.push(reader_(buffer, i));
178
  }
179
  if (theType['be']) {
180
    endianness(buffer, theType['offset']);
181
  }
182
  return values;
183
}
184
185
/**
186
 * @type {!Int8Array}
187
 * @private
188
 */
189
const int8_ = new Int8Array(8);
190
/**
191
 * @type {!Uint32Array}
192
 * @private
193
 */
194
const ui32_ = new Uint32Array(int8_.buffer);
195
/**
196
 * @type {!Float32Array}
197
 * @private
198
 */
199
const f32_ = new Float32Array(int8_.buffer);
200
/**
201
 * @type {!Float64Array}
202
 * @private
203
 */
204
const f64_ = new Float64Array(int8_.buffer);
205
/**
206
 * @type {Function}
207
 * @private
208
 */
209
let reader_;
210
/**
211
 * @type {Function}
212
 * @private
213
 */
214
let writer_;
215
/**
216
 * @type {Object}
217
 * @private
218
 */
219
let gInt_ = {};
220
221
/**
222
 * Turn a byte buffer into what the bytes represent.
223
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
224
 * @param {!Object} theType The type definition.
225
 * @return {number}
226
 * @private
227
 */
228
function readBytes_(buffer, theType, start) {
229
  if (theType['be']) {
230
    endianness(buffer, theType['offset'], start, start + theType['offset']);
231
  }
232
  let value = reader_(buffer, start);
233
  if (theType['be']) {
234
    endianness(buffer, theType['offset'], start, start + theType['offset']);
235
  }
236
  return value;
237
}
238
239
/**
240
 * Turn a byte buffer into what the bytes represent.
241
 * @param {!Array<number|string>|!Uint8Array} buffer An array of bytes.
242
 * @param {!Object} theType The type definition.
243
 * @return {!Array<number>}
244
 * @private
245
 */
246
function fromBytes_(buffer, theType) {
247
  if (theType['be']) {
248
    endianness(buffer, theType['offset']);
249
  }
250
  let len = buffer.length;
251
  let values = [];
252
  len = len - (theType['offset'] - 1);
253
  for (let i=0; i<len; i+=theType['offset']) {
254
    values.push(reader_(buffer, i));
255
  }
256
  return values;
257
}
258
259
/**
260
 * Turn numbers and strings to bytes.
261
 * @param {!Array<number|string>|string} values The data.
262
 * @param {!Object} theType The type definition.
263
 * @return {!Array<number|string>} the data as a byte buffer.
264
 * @private
265
 */
266
function toBytes_(values, theType) {
267
  let j = 0;
268
  let bytes = [];
269
  let len = values.length;
270
  let validate = validateNotNull_;
271
  if (theType['char']) {
272
    validate = validateString_;
273
  }
274
  for(let i=0; i < len; i++) {
275
    validate(values[i], theType);
276
    j = writer_(bytes, values[i], j);
277
  }
278
  if (theType['be']) {
279
    endianness(bytes, theType['offset']);
280
  }
281
  return bytes;
282
}
283
284
/**
285
 * Turn numbers and strings to bytes.
286
 * @param {number|string} value The value to be packed.
287
 * @param {!Object} theType The type definition.
288
 * @param {!Uint8Array|!Array<number>} buffer The buffer to write the bytes to.
289
 * @param {number} index The index to start writing.
290
 * @param {number} len The end index.
291
 * @param {!Function} validate The function used to validate input.
292
 * @param {boolean} be True if big-endian.
293
 * @return {number} the new index to be written.
294
 * @private
295
 */
296
function writeBytes_(value, theType, buffer, index, len, validate, be) {
297
  while (index < len) {
298
    validate(value, theType);
299
    index = writer_(buffer, value, index);
300
  }
301
  if (be) {
302
    endianness(
303
      buffer, theType['offset'], index - theType['offset'], index);
304
  }
305
  return index;
306
}
307
308
/**
309
 * Read int values from bytes.
310
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
311
 * @param {number} i The index to read.
312
 * @return {number}
313
 * @private
314
 */
315
function readInt_(bytes, i) {
316
  return gInt_.read(bytes, i);
317
}
318
319
/**
320
 * Read 1 16-bit float from bytes.
321
 * Thanks https://stackoverflow.com/a/8796597
322
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
323
 * @param {number} i The index to read.
324
 * @return {number}
325
 * @private
326
 */
327
function read16F_(bytes, i) {
328
  let int = gInt_.read(bytes, i);
329
  let exponent = (int & 0x7C00) >> 10;
330
  let fraction = int & 0x03FF;
331
  let floatValue;
332
  if (exponent) {
333
    floatValue =  Math.pow(2, exponent - 15) * (1 + fraction / 0x400);
334
  } else {
335
    floatValue = 6.103515625e-5 * (fraction / 0x400);
336
  }
337
  return floatValue * (int >> 15 ? -1 : 1);
338
}
339
340
/**
341
 * Read 1 32-bit float from bytes.
342
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
343
 * @param {number} i The index to read.
344
 * @return {number}
345
 * @private
346
 */
347
function read32F_(bytes, i) {
348
  ui32_[0] = gInt_.read(bytes, i);
349
  return f32_[0];
350
}
351
352
/**
353
 * Read 1 64-bit float from bytes.
354
 * Thanks https://gist.github.com/kg/2192799
355
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
356
 * @param {number} i The index to read.
357
 * @return {number}
358
 * @private
359
 */
360
function read64F_(bytes, i) {
361
  ui32_[0] = gInt_.read(bytes, i);
362
  ui32_[1] = gInt_.read(bytes, i + 4);
363
  return f64_[0];
364
}
365
366
/**
367
 * Read 1 char from bytes.
368
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
369
 * @param {number} i The index to read.
370
 * @return {string}
371
 * @private
372
 */
373
function readChar_(bytes, i) {
374
  let chrs = '';
375
  for(let j=0; j < gInt_.offset; j++) {
376
    chrs += String.fromCharCode(bytes[i+j]);
377
  }
378
  return chrs;
379
}
380
381
/**
382
 * Write a integer value to a byte buffer.
383
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
384
 * @param {number} number The number to write as bytes.
385
 * @param {number} j The index being written in the byte buffer.
386
 * @return {!number} The next index to write on the byte buffer.
387
 * @private
388
 */
389
function writeInt_(bytes, number, j) {
390
  return gInt_.write(bytes, number, j);
391
}
392
393
/**
394
 * Write one 16-bit float as a binary value.
395
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
396
 * @param {number} number The number to write as bytes.
397
 * @param {number} j The index being written in the byte buffer.
398
 * @return {number} The next index to write on the byte buffer.
399
 * @private
400
 */
401
function write16F_(bytes, number, j) {
402
  f32_[0] = number;
403
  let x = ui32_[0];
404
  let bits = (x >> 16) & 0x8000;
405
  let m = (x >> 12) & 0x07ff;
406
  let e = (x >> 23) & 0xff;
407
  if (e >= 103) {
408
    bits |= ((e - 112) << 10) | (m >> 1);
409
    bits += m & 1;
410
  }
411
  bytes[j++] = bits & 0xFF;
412
  bytes[j++] = bits >>> 8 & 0xFF;
413
  return j;
414
}
415
416
/**
417
 * Write one 32-bit float as a binary value.
418
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
419
 * @param {number} number The number to write as bytes.
420
 * @param {number} j The index being written in the byte buffer.
421
 * @return {number} The next index to write on the byte buffer.
422
 * @private
423
 */
424
function write32F_(bytes, number, j) {
425
  f32_[0] = number;
426
  return gInt_.write(bytes, ui32_[0], j);
427
}
428
429
/**
430
 * Write one 64-bit float as a binary value.
431
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
432
 * @param {number} number The number to write as bytes.
433
 * @param {number} j The index being written in the byte buffer.
434
 * @return {number} The next index to write on the byte buffer.
435
 * @private
436
 */
437
function write64F_(bytes, number, j) {
438
  f64_[0] = number;
439
  j = gInt_.write(bytes, ui32_[0], j);
440
  return gInt_.write(bytes, ui32_[1], j);
441
}
442
443
/**
444
 * Write one char as a byte.
445
 * @param {!Array<number>|!Uint8Array} bytes An array of bytes.
446
 * @param {string} str The string to write as bytes.
447
 * @param {number} j The index being written in the byte buffer.
448
 * @return {number} The next index to write on the byte buffer.
449
 * @private
450
 */
451
function writeChar_(bytes, str, j) {
452
  for (let i=0; i<str.length; i++) {
453
    bytes[j++] = str.charCodeAt(i);
454
  }
455
  return j;
456
}
457
458
/**
459
 * Set the function to unpack the data.
460
 * @param {!Object} theType The type definition.
461
 * @private
462
 */
463
function setReader(theType) {
464
  if (theType['float']) {
465
    if (theType['bits'] == 16) {
466
      reader_ = read16F_;
467
    } else if(theType['bits'] == 32) {
468
      reader_ = read32F_;
469
    } else if(theType['bits'] == 64) {
470
      reader_ = read64F_;
471
    }
472
  } else if (theType['char']) {
473
    reader_ = readChar_;
474
  } else {
475
    reader_ = readInt_;
476
  }
477
}
478
479
/**
480
 * Set the function to pack the data.
481
 * @param {!Object} theType The type definition.
482
 * @private
483
 */
484
function setWriter(theType) {
485
  if (theType['float']) {
486
    if (theType['bits'] == 16) {
487
      writer_ = write16F_;
488
    } else if(theType['bits'] == 32) {
489
      writer_ = write32F_;
490
    } else if(theType['bits'] == 64) {
491
      writer_ = write64F_;
492
    }
493
  } else if (theType['char']) {
494
    writer_ = writeChar_;
495
  } else {
496
    writer_ = writeInt_;
497
  }   
498
}
499
500
/**
501
 * Validate the type and set up the packing/unpacking functions.
502
 * @param {!Object} theType The type definition.
503
 * @throws {Error} If the type definition is not valid.
504
 * @private
505
 */
506
function setUp_(theType) {
507
  validateType_(theType);
508
  theType['offset'] = theType['bits'] < 8 ? 1 : Math.ceil(theType['bits'] / 8);
509
  setReader(theType);
510
  setWriter(theType);
511
  if (!theType['char']) {
512
    gInt_ = new Integer(
513
      theType['bits'] == 64 ? 32 : theType['bits'],
514
      theType['float'] ? false : theType['signed']);
515
  } else {
516
    // Workaround; should not use Integer when type['char']
517
    gInt_.offset = theType['bits'] < 8 ? 1 : Math.ceil(theType['bits'] / 8);
518
  }
519
}
520
521
/**
522
 * Validate the type definition.
523
 * @param {!Object} theType The type definition.
524
 * @throws {Error} If the type definition is not valid.
525
 * @private
526
 */
527
function validateType_(theType) {
528
  if (!theType) {
529
    throw new Error('Undefined type.');
530
  }
531
  if (theType['float']) {
532
    validateFloatType_(theType);
533
  } else {
534
    if (theType['char']) {
535
      validateCharType_(theType);
536
    } else {
537
      validateIntType_(theType);
538
    }
539
  }
540
}
541
542
/**
543
 * Validate the type definition of floating point numbers.
544
 * @param {!Object} theType The type definition.
545
 * @throws {Error} If the type definition is not valid.
546
 * @private
547
 */
548
function validateFloatType_(theType) {
549
  if ([16,32,64].indexOf(theType['bits']) == -1) {
550
    throw new Error('Not a supported float type.');
551
  }
552
}
553
554
/**
555
 * Validate the type definition of char and strings.
556
 * @param {!Object} theType The type definition.
557
 * @throws {Error} If the type definition is not valid.
558
 * @private
559
 */
560
function validateCharType_(theType) {
561
  if (theType['bits'] < 8 || theType['bits'] % 2) {
562
    throw new Error('Wrong offset for type char.');
563
  }
564
}
565
566
/**
567
 * Validate the type definition of integers.
568
 * @param {!Object} theType The type definition.
569
 * @throws {Error} If the type definition is not valid.
570
 * @private
571
 */
572
function validateIntType_(theType) {
573
  if (theType['bits'] < 1 || theType['bits'] > 53) {
574
    throw new Error('Not a supported type.');
575
  }
576
}
577
578
/**
579
 * Validate strings with bad length.
580
 * @param {string|number} value The string to validate.
581
 * @param {!Object} theType The type definition.
582
 * @private
583
 */
584
function validateString_(value, theType) {
585
  validateNotNull_(value);
586
  if (value.length > theType['offset']) {
587
    throw new Error('String is bigger than its type definition.');
588
  } else if (value.length < theType['offset']) {
589
    throw new Error('String is smaller than its type definition.');
590
  }
591
}
592
593
/**
594
 * Validate that the value is not null.
595
 * @param {string|number} value The value.
596
 * @private
597
 */
598
function validateNotNull_(value) {
599
  if (value === null || value === undefined) {
600
    throw new Error('Cannot pack null or undefined values.');
601
  }
602
}
603